package br.mikhas.container.annotation;

import br.mikhas.container.application.ApplicationScope;
import br.mikhas.container.config.BeanDefinition;
import br.mikhas.container.config.ResourceReferenceDependency;
import br.mikhas.container.config.StandadBeanDefinition;
import br.mikhas.container.config.ValueDependecy;
import br.mikhas.container.config.Dependency.DependencyTarget;
import br.mikhas.container.factory.PrototypeScope;
import br.mikhas.container.scope.ScopeProxyMethodHandler;
import br.mikhas.reflector.ProxyUtils;

public class ScopedBeanDefinitionExtractor implements
		ChainedDefinitionExtractor {

	@Override
	public BeanDefinition extractDefinition(Class<?> cls,
			BeanDefinition definition, AnnotatedBeanProvider provider) {

		if (definition instanceof AnnotatedBeanDefinition
				&& isScoped(definition)) {
			createScopeProxyDefinition(provider, definition);
		}
		return definition;
	}

	private boolean isScoped(BeanDefinition definition) {
		String scope = definition.getScope();

		return !scope.equals(ApplicationScope.APPLICATION_SCOPE)
				&& !scope.equals(PrototypeScope.PROTOTYPE_SCOPE);
	}

	private void createScopeProxyDefinition(AnnotatedBeanProvider provider,
			BeanDefinition definition) {

		String scopeName = definition.getScope();
		String beanName = definition.getName();
		String scopedBeanName = scopeName + "::" + beanName;
		String handlerName = scopedBeanName + "::ScopeHandler";

		// Handler definition
		StandadBeanDefinition handler = new StandadBeanDefinition(handlerName,
				ScopeProxyMethodHandler.class);

		ValueDependecy scopedBeanDependency = new ValueDependecy(String.class,
				DependencyTarget.Property);
		scopedBeanDependency.setPropertyName("resourceName");
		scopedBeanDependency.setValue(scopedBeanName);

		handler.addDependency(scopedBeanDependency);

		// Proxy definition
		Class<?> proxyClass = ProxyUtils.createProxyClass(definition
				.getInstantiatingClass(), Object.class.getDeclaredMethods());

		StandadBeanDefinition proxy = new StandadBeanDefinition(beanName,
				definition.getType(), proxyClass);

		ResourceReferenceDependency handlerDependency = new ResourceReferenceDependency(
				ScopeProxyMethodHandler.class, DependencyTarget.Property);
		handlerDependency.setPropertyName("handler");
		handlerDependency.setReferenceName(handlerName);
		proxy.addDependency(handlerDependency);

		provider.addBeanDefinition(proxy);
		provider.addBeanDefinition(handler);

		if (definition instanceof AnnotatedBeanDefinition) {
			AnnotatedBeanDefinition adef = (AnnotatedBeanDefinition) definition;
			adef.setName(scopedBeanName);
		}

	}
}
